// 数组响应式
// 1. 克隆数组原型
const originArrayProto = Array.prototype;
const copyArrayProto = Object.create(originArrayProto);
const arrayReactiveMethods = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse",
];
arrayReactiveMethods.forEach((method) => {
  // 2. 覆盖数组原型方法
  copyArrayProto[method] = function () {
    // 2.1 先执行原始操作
    originArrayProto[method].apply(this, arguments);
    // 2.2 再增添新操作：通知更新
    console.log(`数组执行${method}更新了`, this);
  };
});


function observe(obj) {
  if (typeof obj !== "object" || obj === null) {
    return;
  }

  if (Array.isArray(obj)) {
    // 数组响应式
    obj.__proto__ = copyArrayProto
  }

  // 对象响应式
  return new Observe(obj);
}

class Observe {
  constructor(value) {
    this.walk(value);
  }
  walk(obj) {
    Object.keys(obj).forEach((key) => {
      defineReactive(obj, key, obj[key]);
    });
  }
}

function defineReactive(obj, key, val) {
  // 1. 递归
  observe(val);
  // 2. 通过 Object.defineProperty 的 get 和 set 拦截data对象的每一个属性，做一些事情
  // 2.1 一个data中的key对应一个Dep
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      // 2.2 一个页面中对data的key的引用对应一个Watcher, 将Dep.target添加到dep中
      Dep.target && dep.addDep(Dep.target);
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        // 如果newVal是对象，也要做响应式处理
        observe(newVal);
        val = newVal;
        // 2.3 执行每一个Watcher的update方法
        dep.notify();
      }
    },
  });
}

function proxy(vm, key) {
  Object.keys(vm[key]).forEach((k) => {
    Object.defineProperty(vm, k, {
      get() {
        return vm[key][k];
      },
      set(v) {
        vm[key][k] = v;
      },
    });
  });
}

class XVue {
  constructor(options) {
    this.$options = options;
    this.$data = options.data;
    this.$el = options.el;
    // 1. 拦截监听data选项
    observe(this.$data);
    // 2. 代理 data 对象
    proxy(this, "$data");
    // 3. 编译模板
    new Compile(this.$el, this);
    // 4. 执行created
    if (options.created) {
      options.created.call(this);
    }
  }
}

// 收集依赖: 一个data中的key对应一个Dep
class Dep {
  constructor() {
    this.deps = [];
  }
  addDep(dep) {
    this.deps.push(dep);
  }
  notify() {
    this.deps.forEach((dep) => dep.update());
  }
}

// 观察器：一个页面中对data的key的引用对应一个Watcher[一个Dep数组可能有多个同名的Watcher]
class Watcher {
  constructor(vm, key, cb) {
    this.vm = vm;
    this.key = key;
    this.cb = cb;

    // 将Dep静态属性target指向当前watcher实例
    Dep.target = this;
    // 触发getter，添加依赖 Dep 实例
    this.vm[this.key];
    // 回收
    Dep.target = null;
  }

  update() {
    this.cb.call(this.vm, this.vm[this.key]);
  }
}